/*
 * Atlunix Php decoder by David GUGLIELMI
 *
 * some part of this program are extract from Minicode project
 * http://sourceforge.net/projects/microcode/
 *
 * This program is a free software released as-is.
 *
 */

#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstring>

#include <mcrypt.h>

#define DATABAY_DES_ECB    20 /* DES Electronic Code  Book  (ECB)  mode */
#define DATABAY_DES_CBC    21 /* DES Cipher Block Chaining (CBC) mode */
#define DATABAY_3DES_ECB   22 /* Triple DES ECB mode */
#define DATABAY_3DES_CBC   23 /* Triple DES CBC mode */
#define DATABAY_AES_ECB    30 /* AES Cipher Rijndael 128bit */
#define DATABAY_AES_CBC    31 

#define FLAGSTART length-flaglength-7
#define FOOTERSTART length-keylength-flaglength-7

#define DESPREFIX "1hjkmkkjnadjfagkhjlfykoojfgklsmjg"

void print_usage (void);
std::string decode_file (char**, int);
MCRYPT databay_init_crypto (int *, char *, char *, char **, char *, int *);

int
main (int argc, char *argv [])
{


  if (argc < 2)
    print_usage ();

  std::ifstream file (argv[1]);
  
  if (file.is_open ())
   {

    file.seekg (0, std::ios::end);
    int length = file.tellg();
    file.seekg (0, std::ios::beg);

    char *buffer = new char [length];

    file.read (buffer, length);
    std::cout << "<?" << std::endl << std::endl
              << decode_file (&buffer, length) << std::endl
              << "?>" << std::endl << std::endl;

    delete []buffer;

    file.close ();

   }
  else
    std::cout << "File doesn't exist" << std::endl;

  return EXIT_SUCCESS;

}

std::string
decode_file (char **buffer, int length)
{

  MCRYPT td = 0;
  char *key = NULL;
  char *tag;
  char *flags;
  char *mybuffer = *buffer;
	
  int blocksize;
  char *IV = NULL;
  int keylength = 0;
  int flaglength;
  int algorithm;
  int outbuflen = 0;

  int i;

  tag = new char [3 * sizeof(char)];
  std::memcpy (tag, &(mybuffer [length - 2]), 2);
  tag[2] = '\0';

  algorithm = std::strtol (tag, NULL, 10);
  delete []tag;

  switch (algorithm)
   {
    case DATABAY_DES_ECB:
    case DATABAY_DES_CBC:
      keylength = 8;
      break;
    case DATABAY_3DES_ECB:
    case DATABAY_3DES_CBC:
      keylength = 24;
      break;

    case DATABAY_AES_CBC:
    case DATABAY_AES_ECB:
      keylength = 32;
      break;

    default:
      exit (EXIT_FAILURE);
   }


  flaglength = (unsigned int)mybuffer[length - 7];

  flags = new char [(flaglength + 1) * sizeof(char)];

  for (i = 0; i < flaglength; i++) 
    flags[i]= mybuffer[FLAGSTART + i];

  key = new char [(keylength + 1) * sizeof(char)];

  if (flags[0] && 128) 
   { 
    std::memcpy (key, DESPREFIX, keylength / 2);
    std::strncpy (&(key[keylength / 2]),\
                  &(mybuffer[FOOTERSTART + keylength / 2]), keylength / 2);
	
    key[keylength] = '\0';
   }
  else 
    exit(EXIT_FAILURE);

  switch (algorithm)
   {
    case DATABAY_DES_ECB:
      td = databay_init_crypto (&blocksize, (char*)MCRYPT_DES,\
                                (char*)MCRYPT_ECB, &IV, key,&keylength);
      break;
    case DATABAY_DES_CBC:
     td = databay_init_crypto (&blocksize, (char*)MCRYPT_DES,\
                               (char*)MCRYPT_CBC, &IV, key, &keylength);
     break;
    case DATABAY_AES_ECB:
     td = databay_init_crypto (&blocksize, (char*)MCRYPT_RIJNDAEL_128,\
                               (char*)MCRYPT_ECB, &IV, key, &keylength);
      break;
    case DATABAY_AES_CBC:
      td = databay_init_crypto (&blocksize, (char*)MCRYPT_RIJNDAEL_128,\
                                (char*)MCRYPT_CBC, &IV, key, &keylength);
      break;
    case DATABAY_3DES_ECB:
      td = databay_init_crypto (&blocksize, (char*)MCRYPT_3DES,\
                                (char*)MCRYPT_ECB, &IV, key, &keylength);
      break;
    case DATABAY_3DES_CBC:
      td = databay_init_crypto (&blocksize, (char*)MCRYPT_3DES,\
                                (char*)MCRYPT_CBC, &IV, key, &keylength);
      break;      
    }
  
  outbuflen = FOOTERSTART;
  mdecrypt_generic (td, *buffer, outbuflen);
  mybuffer = *buffer;

  outbuflen--;
  
  while (mybuffer[outbuflen] == '\0')
    outbuflen--;
  
  mcrypt_generic_deinit (td);
  mcrypt_module_close (td);

  delete []key;
  delete []flags;

  return std::string (mybuffer);

}

void
print_usage (void)
{

  std::cout << "Usage: decoder CRYPTED_FILE" << std::endl;

  exit (EXIT_FAILURE);

}

MCRYPT
databay_init_crypto (int *blocksize, char *ciphername, char *ciphermode,\
                     char **IV, char *key, int *keysize)
{

  int i;
  int error = 0;
  char *myIV;
  MCRYPT td;

  td = mcrypt_module_open(ciphername, (char*)"", ciphermode, (char*)"");

  if (td == MCRYPT_FAILED) 
    exit(EXIT_FAILURE);

  myIV = *IV;
  myIV = new char [mcrypt_enc_get_iv_size (td)];
  
  for (i = 0; i < mcrypt_enc_get_iv_size (td); i++) 
    myIV[i] = i;
  
  *blocksize = mcrypt_enc_get_block_size (td);

  error = mcrypt_generic_init (td, key, mcrypt_enc_get_key_size (td), myIV);

  delete []myIV;

  if (error < 0) 
   {
    mcrypt_perror (error);
    exit (EXIT_FAILURE);
   }

  return td;

}

